#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "IconHelper/iconhelper.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow),
    plotting(false),
    PLOT_NUMBER_OF_POINTS(5000),
    PLOT_LINE_PEN_WIDTH(1.5),
    PLOT_LEGEND_BRIGHTLESS(150),
    dataPointNumber(0)
{
    ui->setupUi(this);

    // Load ini
    loadConfigFile();

    // Setup ui
    initLeftMain();
    initTreeView();
    initPlotGraphView();

    // Tool bar
    mCommConfig = new CommConfig();

    // Signal&slot
    qRegisterMetaType<MESSAGE>("MESSAGE");
    connect(mCommConfig,&CommConfig::newSigMavMsg,this,&MainWindow::newMessageArrived);
}

MainWindow::~MainWindow()
{
    delete ui;
}


/****************************************************
 _      ____         _    _    _
/ |    / ___|   ___ | |_ | |_ (_) _ __    __ _
| |    \___ \  / _ \| __|| __|| || '_ \  / _` |
| | _   ___) ||  __/| |_ | |_ | || | | || (_| |
|_|(_) |____/  \___| \__| \__||_||_| |_| \__, |
                                         |___/
*****************************************************/

///
/// \brief MainWindow::loadConfigFile
/// \return
///
void MainWindow::loadConfigFile(void)
{
    QString iniName = QCoreApplication::applicationDirPath() + "/Settings.ini";

    //"Settings.ini"，open the file if exist,otherwise creat it!
    QSettings *iniSetting = new QSettings(iniName,QSettings::IniFormat);

    // Read from ini file
    // Theme
    ui->comboBoxSystemThemeColor->setCurrentText(iniSetting->value("/THEME/color").toString());
    ui->comboBoxSystemThemeFontsSize->setCurrentText(iniSetting->value("/THEME/fontsize").toString());
    mThemeStyle = iniSetting->value("/THEME/color").toString();

    // Analysis

    delete iniSetting;
}

///
/// \brief MainWindow::on_pushButtonSettingSave_released
///
void MainWindow::on_pushButtonSettingSave_released()
{
    QString iniName = QCoreApplication::applicationDirPath() + "/Settings.ini";

    //"Settings.ini"，open the file if exist,otherwise creat it!
    QSettings *iniSetting = new QSettings(iniName,QSettings::IniFormat);

    // Write to ini file
    // Theme
    iniSetting->beginGroup("THEME");
    iniSetting->setValue("color",ui->comboBoxSystemThemeColor->currentText());
    iniSetting->setValue("fontsize",ui->comboBoxSystemThemeFontsSize->currentText());
    iniSetting->endGroup();

    delete iniSetting;

    QString dlgTitle="QMavPlotter(QMP)";
    QString strInfo="Save Configrations successfully!";
    QMessageBox::information(this, dlgTitle, strInfo,
                              QMessageBox::Ok,QMessageBox::NoButton);

}

/*********************************************************
 ____       __  __         _         _   _
|___ \     |  \/  |  __ _ (_) _ __  | \ | |  __ _ __   __
  __) |    | |\/| | / _` || || '_ \ |  \| | / _` |\ \ / /
 / __/  _  | |  | || (_| || || | | || |\  || (_| | \ V /
|_____|(_) |_|  |_| \__,_||_||_| |_||_| \_| \__,_|  \_/
*********************************************************/
///
/// \brief MainWindow::initLeftMain
///
void MainWindow::initLeftMain()
{

    ui->widgetLeftMain->setProperty("flag", "left");

    // Set button icon
    pixCharMain << 0xf201 << 0xf040 << 0xf0ce << 0xf013 << 0xf0c1;

    // Button list
    btnsMain << ui->tbtnAnalysis << ui->tbtnOperate << ui->tbtnMonitor << ui->tbtnSetting << ui->tbtnConnect;

    // Init all button
    int count = btnsMain.count();
    for (int i = 0; i < count; i++) {
        btnsMain.at(i)->setCheckable(true);
        btnsMain.at(i)->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
        connect(btnsMain.at(i), SIGNAL(clicked(bool)), this, SLOT(leftMainClick()));
    }

    IconHelper::Instance()->setStyle(ui->widgetLeftMain, btnsMain, pixCharMain, 20, 35, 25, "left", 4);

    // Default open the monitor page
    ui->tbtnMonitor->click();
}

///
/// \brief MainWindow::leftMainClick
///
void MainWindow::leftMainClick()
{
    QToolButton *b = (QToolButton *)sender();
    QString name = b->text();

    int count = btnsMain.count();
    for (int i = 0; i < count; i++) {
        if (btnsMain.at(i) == b) {
            btnsMain.at(i)->setChecked(true);
            btnsMain.at(i)->setIcon(QIcon(IconHelper::Instance()->getPixmap(btnsMain.at(i), false)));
        } else {
            btnsMain.at(i)->setChecked(false);
            btnsMain.at(i)->setIcon(QIcon(IconHelper::Instance()->getPixmap(btnsMain.at(i), true)));
        }
    }

    if (name == "Analysis") {
        ui->stackedWidget->setCurrentIndex(0);
    } else if (name == "Operate") {
        ui->stackedWidget->setCurrentIndex(1);
    } else if (name == "Monitor") {
        ui->stackedWidget->setCurrentIndex(2);
    } else if (name == "Setting") {
        ui->stackedWidget->setCurrentIndex(3);
    } else if (name == "Connect") {
        mCommConfig->show();
    }
}

/**********************************************************
 _____        _                   _              _
|___ /       / \    _ __    __ _ | | _   _  ___ (_) ___
  |_ \      / _ \  | '_ \  / _` || || | | |/ __|| |/ __|
 ___) |_   / ___ \ | | | || (_| || || |_| |\__ \| |\__ \
|____/(_) /_/   \_\|_| |_| \__,_||_| \__, ||___/|_||___/
                                     |___/
**********************************************************/
///
/// \brief MainWindow::initPlotGraphView
///
void MainWindow::initPlotGraphView()
{
    //设置背景颜色
    if("Light" == mThemeStyle) {
        ui->plot->setBackground(QBrush("#FAFAFA"));                // Background for the plot area
    }else{
        ui->plot->setBackground(QBrush("#19232D"));                // Background for the plot area
    }


    //设置x-y轴参数
    ui->plot->clearItems();                                         // Remove everything from the plot
    ui->plot->yAxis->setTickStep(10);                               // Set tick step according to user spin box
    ui->plot->yAxis->setRange(-200, 200);                           // Set lower and upper plot range
    ui->plot->xAxis->setRange(0, 1000);                             // Set x axis range for specified number of points
    ui->plot->setPlottingHints(QCP::phFastPolylines);               // Graph/Curve lines are drawn with a faster method.

    //设置为高性能绘图模式
    ui->plot->setNotAntialiasedElements(QCP::aeAll);                // used for higher performance (see QCustomPlot real time example)

    //设置字体
    QFont font;
    font.setPixelSize(14);
    font.setFamily("Verdana, Tahoma, Arial");
    font.setStyleStrategy(QFont::NoAntialias);
    ui->plot->xAxis->setTickLabelFont(font);
    ui->plot->yAxis->setTickLabelFont(font);
    ui->plot->legend->setFont(font);

    //设置刻度（自动大小、颜色）
    ui->plot->yAxis->setAutoTickStep(true);                                               // User can change tick step with a spin box
    ui->plot->yAxis->setTickStep(10);                                                     // Set initial tick step
    ui->plot->xAxis->setTickLabelColor(QColor(170,170,170));                              // Tick labels color
    ui->plot->yAxis->setTickLabelColor(QColor(170,170,170));                              // See QCustomPlot examples / styled demo

    //设置主、副网格属性（颜色、可见性）
    ui->plot->xAxis->grid()->setPen(QPen(QColor(170,170,170), 1, Qt::NoPen));
    ui->plot->yAxis->grid()->setPen(QPen(QColor(170,170,170), 1, Qt::DotLine));
    ui->plot->xAxis->grid()->setSubGridPen(QPen(QColor(80,80,80), 1, Qt::NoPen));
    ui->plot->yAxis->grid()->setSubGridPen(QPen(QColor(80,80,80), 1, Qt::NoPen));
    ui->plot->xAxis->grid()->setSubGridVisible(true);
    ui->plot->yAxis->grid()->setSubGridVisible(true);

    //设置x-y轴（颜色、箭头、鼠标拖拽、滚轮缩放）
    ui->plot->xAxis->setBasePen(QPen(QColor(170,170,170)));
    ui->plot->yAxis->setBasePen(QPen(QColor(170,170,170)));
    ui->plot->xAxis->setTickPen(QPen(QColor(170,170,170)));
    ui->plot->yAxis->setTickPen(QPen(QColor(170,170,170)));
    ui->plot->xAxis->setSubTickPen(QPen(QColor(170,170,170)));
    ui->plot->yAxis->setSubTickPen(QPen(QColor(170,170,170)));
    ui->plot->xAxis->setUpperEnding(QCPLineEnding::esSpikeArrow);
    ui->plot->yAxis->setUpperEnding(QCPLineEnding::esSpikeArrow);
    ui->plot->setInteraction(QCP::iRangeDrag, true);
    ui->plot->setInteraction(QCP::iRangeZoom, true);
    ui->plot->setCursor(Qt::CursorShape::CrossCursor);

    // 设置图例行优先排列
    ui->plot->legend->setFont(font);
    ui->plot->legend->setVisible(true);
    ui->plot->plotLayout()->setRowStretchFactor(1, 0.001);
    ui->plot->legend->setBorderPen(Qt::NoPen);
    ui->plot->legend->setBrush(QColor(255,255,255,PLOT_LEGEND_BRIGHTLESS));

    //鼠标事件
    connect(ui->plot, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(mousePress(QMouseEvent*)));
    connect(ui->plot, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(mouseMove(QMouseEvent*)));
    connect(ui->plot, SIGNAL(mouseRelease(QMouseEvent*)), this, SLOT(mouseRelease(QMouseEvent*)));

    //初始化QRubberBand
    rubberBand = new QRubberBand(QRubberBand::Rectangle, ui->plot);
}

///
/// \brief MainWindow::replotGraph
/// \param msg
///
void MainWindow::replotGraph(MESSAGE &msg)
{
    QMap<QString,OPTION>::iterator i;

    if(plotting){
        static qint64 startPointNumber = QDateTime::currentDateTime().toMSecsSinceEpoch();
        dataPointNumber = QDateTime::currentDateTime().toMSecsSinceEpoch() - startPointNumber;

        for(int j = 0; j < msg.nums;j++){

            QString fid = QString("%1::%2::%3").arg(msg.sysid).arg(msg.msgid).arg(j);

            for(i = mOptionMap.begin();i!= mOptionMap.end();i++){

                if(QString::compare(fid,i.key()) == 0){
                    //设置颜色、线宽
                    QPen graphPen(i.value().color);
                    graphPen.setWidthF(PLOT_LINE_PEN_WIDTH);

                    //按照颜色绘制实时数据
                    ui->plot->graph(i.value().layerID)->setPen(QPen(graphPen));
                    ui->plot->graph(i.value().layerID)->addData(dataPointNumber, msg.fields[j].rtValue);
                    ui->plot->graph(i.value().layerID)->removeDataBefore(dataPointNumber - PLOT_NUMBER_OF_POINTS);
                    ui->plot->graph(i.value().layerID)->setName(i.value().tag);
                }
            }
        }

        ui->plot->xAxis->setRange(dataPointNumber - PLOT_NUMBER_OF_POINTS, dataPointNumber);

        ui->plot->replot(QCustomPlot::rpQueued);
    }else{

    }
}

///
/// \brief MainWindow::on_pushButtonPlot_released
///
void MainWindow::on_pushButtonPlot_released()
{
    if(plotting) {
        plotting = false;// Stop plotting                                                            // Stop updating plot timer
        ui->pushButtonPlot->setText("Start Plot");
    } else {                                                                                         // Start plotting
        plotting = true;                                                                             // Start updating plot timer
        ui->pushButtonPlot->setText("Stop Plot");
    }
}

///
/// \brief MainWindow::on_pushButtonReset_released
///
void MainWindow::on_pushButtonReset_released()
{
    ui->plot->yAxis->setRange(-1000, 1000);
    ui->plot->xAxis->setRange(dataPointNumber - PLOT_NUMBER_OF_POINTS, dataPointNumber);
    ui->plot->yAxis->setTickStep(100);
    ui->plot->replot(QCustomPlot::rpQueued);
}

///
/// \brief MainWindow::on_cbox_axis_x_pointes_currentIndexChanged
/// \param arg1
///
void MainWindow::on_cbox_axis_x_pointes_currentIndexChanged(const QString &arg1)
{
    QStringList list_time = arg1.split(' ');
    QString pointCnt = list_time.at(0);
    QString pointunit = list_time.at(1);

    if ((pointunit.compare("second") == 0) || (pointunit.compare("seconds") == 0)){
        PLOT_NUMBER_OF_POINTS = pointCnt.toInt()*1000;
    }
    if ((pointunit.compare("minute") == 0) || (pointunit.compare("minutes") == 0)){
        PLOT_NUMBER_OF_POINTS = pointCnt.toInt()*1000*60;
     }

    ui->plot->xAxis->setRange(dataPointNumber - PLOT_NUMBER_OF_POINTS, dataPointNumber);
    ui->plot->replot(QCustomPlot::rpQueued);
}

///
/// \brief MainWindow::mousePress
/// \param mevent
///
void MainWindow::mousePress(QMouseEvent* mevent)
{
    if(mevent->button() == Qt::RightButton && plotting == false)
    {
        rubberOrigin = mevent->pos();
        rubberBand->setGeometry(QRect(rubberOrigin, QSize()));
        rubberBand->show();
    }
}

///
/// \brief MainWindow::mouseMove
/// \param mevent
///
void MainWindow::mouseMove(QMouseEvent *mevent)
{
    if(rubberBand->isVisible() && plotting == false)
            rubberBand->setGeometry(QRect(rubberOrigin, mevent->pos()).normalized());
}

///
/// \brief MainWindow::mouseRelease
/// \param mevent
///
void MainWindow::mouseRelease(QMouseEvent *mevent)
{

    Q_UNUSED(mevent);
    if (rubberBand->isVisible() && plotting == false)
    {
        const QRect zoomRect = rubberBand->geometry();
        int xp1, yp1, xp2, yp2;
        zoomRect.getCoords(&xp1, &yp1, &xp2, &yp2);
        double x1 = ui->plot->xAxis->pixelToCoord(xp1);
        double x2 = ui->plot->xAxis->pixelToCoord(xp2);
        double y1 = ui->plot->yAxis->pixelToCoord(yp1);
        double y2 = ui->plot->yAxis->pixelToCoord(yp2);

        ui->plot->xAxis->setRange(x1, x2);
        ui->plot->yAxis->setRange(y1, y2);

        rubberBand->hide();
        ui->plot->replot();
    }
}

/***********************************************************
 _  _       _____                         _
| || |     |_   _|_ __  ___   ___ __   __(_)  ___ __      __
| || |_      | | | '__|/ _ \ / _ \\ \ / /| | / _ \\ \ /\ / /
|__   _|_    | | | |  |  __/|  __/ \ V / | ||  __/ \ V  V /
   |_| (_)   |_| |_|   \___| \___|  \_/  |_| \___|  \_/\_/

***********************************************************/

///
/// \brief MainWindow::initTreeView
///
void MainWindow::initTreeView()
{
    //1，QTreeView常用设置项
    QTreeView* t = ui->treeView;
//    t->setEditTriggers(QTreeView::NoEditTriggers);          //单元格不能编辑
    t->setSelectionBehavior(QTreeView::SelectRows);         //一次选中整行
    t->setSelectionMode(QTreeView::SingleSelection);        //单选，配合上面的整行就是一次选单行
    t->setAlternatingRowColors(true);                       //每间隔一行颜色不一样，当有qss时该属性无效
    t->setFocusPolicy(Qt::NoFocus);                         //去掉鼠标移到单元格上时的虚线框

    //2，列头相关设置
    t->header()->setHighlightSections(true);                //列头点击时字体变粗，去掉该效果
    t->header()->setDefaultAlignment(Qt::AlignCenter);      //列头文字默认居中对齐
    t->header()->setDefaultSectionSize(160);                //默认列宽100
    t->header()->setStretchLastSection(true);               //最后一列自适应宽度
    t->header()->setSortIndicator(0,Qt::AscendingOrder);    //按第1列升序排序

    //3，构造Model
    mModel = new TreeModel(nullptr);
    parents << mModel->root();
    ui->treeView->setModel(mModel);

    //4，应用model
    t->setModel(mModel);

    //5, 信号槽，右键菜单
    t->setContextMenuPolicy(Qt::CustomContextMenu);
    connect(t, &QTreeView::customContextMenuRequested, this, &MainWindow::slotTreeMenu);
//    connect(&updateTimer, SIGNAL(timeout()), this, SLOT(replot()));
}

///
/// \brief MainWindow::slotTreeMenu
/// \param pos
///
void MainWindow::slotTreeMenu(const QPoint &pos)
{

    QMenu menu;

    QModelIndex curIndex = ui->treeView->indexAt(pos);      //当前点击的元素的index
    QModelIndex index = curIndex.sibling(curIndex.row(),0); //该行的第1列元素的index

    if (index.isValid()){
        //添加一行菜单，进行展开
        menu.addAction(QStringLiteral("Change pencil color"), this, SLOT(slotTreeMenuChangeColor(bool)));
        menu.addSeparator();    //添加一个分隔线
        menu.addAction(QStringLiteral("Add/Remove from plot"), this, SLOT(slotTreeMenuAddToPlot(bool)));
        menu.addSeparator();    //添加一个分隔线
        menu.addAction(QStringLiteral("Edit the expression"), this, SLOT(slotTreeMenuEditExpression(bool)));

    }
    menu.exec(QCursor::pos());  //显示菜单
}

///
/// \brief MainWindow::slotTreeMenuAddToPlot
/// \param checked
///
void MainWindow::slotTreeMenuAddToPlot(bool checked)
{
    Q_UNUSED(checked);
    QModelIndex curIndex = ui->treeView->currentIndex();
    QModelIndex index = curIndex.sibling(curIndex.row(),0); //同一行第一列元素的index

    QString fid = mModel->itemFromIndex(index)->data(2).toString();
    QString tag = mModel->itemFromIndex(index)->parentItem()->data(0).toString()+":"+ mModel->itemFromIndex(index)->data(0).toString();

    if(index.isValid() && !mOptionMap.contains(fid)){

        // Alloc graph layel
        ui->plot->addGraph();

        // Insert to map
        OPTION op;
        op.color = QColor::fromHsl(qrand()%12*30,255,128);
        op.formula = "";
        op.layerID = ui->plot->graphCount() - 1;
        op.tag     = tag;
        mOptionMap.insert(fid,op);

    }else{

        // Remove the graph from gird
        ui->plot->removeGraph(mOptionMap.find(fid).value().layerID);

        // Remove the option from map
        mOptionMap.remove(fid);
    }

    if(!plotting){emit ui->pushButtonPlot->released();}


}

///
/// \brief MainWindow::slotTreeMenuChangeColor
/// \param checked
///
void MainWindow::slotTreeMenuChangeColor(bool checked)
{
    Q_UNUSED(checked);
    QModelIndex curIndex = ui->treeView->currentIndex();
    QModelIndex index = curIndex.sibling(curIndex.row(),0); //同一行第一列元素的index

    QString fid = mModel->itemFromIndex(index)->data(2).toString();

    if(index.isValid() && mOptionMap.contains(fid))
    {
        // Insert to map
        OPTION op = mOptionMap.find(fid).value();
        op.color = QColor::fromHsl(qrand()%12*30,255,128);
        mOptionMap.insert(fid,op);
    }
}

///
/// \brief MainWindow::slotTreeMenuEditExpression
/// \param checked
///
void MainWindow::slotTreeMenuEditExpression(bool checked)
{
    Q_UNUSED(checked);
    QModelIndex curIndex = ui->treeView->currentIndex();
    QModelIndex index = curIndex.sibling(curIndex.row(),0); //同一行第一列元素的index

    QString fid = mModel->itemFromIndex(index)->data(2).toString();

    if(index.isValid() && mOptionMap.contains(fid))
    {
        // Insert to map
        OPTION op = mOptionMap.find(fid).value();

        QString dlgTitle     = "QMavPlotter(QMP)";
        QString txtLabel     = "Expression";
        QRegExp separators    {"[-+*/()]"};
        QString defaultInput = op.formula;
        QLineEdit::EchoMode echoMode = QLineEdit::Normal;
        //QLineEdit::EchoMode echoMode=QLineEdit::Password;//密码输入
        bool ok = false;
        QString text = QInputDialog::getText(this, dlgTitle,txtLabel, echoMode,defaultInput, &ok);
        if (ok && !text.isEmpty()){
            QStringList columnStrings = text.split(QRegExp("[-+*/()](\b)"));
            op.formula = text;
        }
        mOptionMap.insert(fid,op);
    }
}

///
/// \brief MainWindow::insertNodeMsg
/// \param msg
///
void MainWindow::insertNodeMsg(MESSAGE &msg)
{
    QString id = QString(msg.sysid) + QString(msg.msgid);

    // Insert the new message
    if(!mMsgMap.contains(id)){
        // Insert
        mMsgMap.insert(id,msg);

        // Update the Treeview
        QString node_msg;
        node_msg = QString("M%1::%2").arg(msg.sysid).arg(msg.name);

        QList<QVariant> columnData;
        columnData << node_msg << "" << "";

        // Append a new item to the current parent's list of children.
        TreeItem *node = new TreeItem(columnData, parents.last());
        parents.last()->appendChild(node);
        parents << node;

        // Insert all fileds in the message mode
        for(int i = 0; i < msg.nums ; i++){

            // Generate field infomations.
            QList<QVariant> columnFieldData;
            columnFieldData << msg.fields.at(i).name << msg.fields.at(i).rtValue << QString("%1::%2::%3").arg(msg.sysid).arg(msg.msgid).arg(i);

            // Append a new item to the current parent's list of children.
            parents.last()->appendChild(new TreeItem(columnFieldData, parents.last()));
        }

        // Break from current message node
        parents.pop_back();

    }else{
        // Update
        mMsgMap.insert(id,msg);

        // Update the Treeview
        QString node_msg;
        node_msg = QString("M%1::%2").arg(msg.sysid).arg(msg.name);

        // Find the message item in the parent's list of children.
        int index = 0;
        for(int i = 0;i < mModel->root()->childCount(); i++){
            if(QString::compare(mModel->root()->child(i)->data(0).toString(),node_msg) == 0){
                index = i;
                break;
            }
        }
        TreeItem *node =  mModel->root()->child(index);

        // Insert all fileds in the message mode
        for(int i = 0; i < node->childCount() ; i++){

            // Generate field infomations.
            QList<QVariant> columnFieldData;
            columnFieldData << msg.fields.at(i).name << msg.fields.at(i).rtValue << QString("%1::%2::%3").arg(msg.sysid).arg(msg.msgid).arg(i);

            // Append a new item to the current parent's list of children.
            node->modifyChild(i,new TreeItem(columnFieldData, node));
        }
    }

    static qint64 timestamp_last = 0;
    qint64 timestamp = QDateTime::currentDateTime().toMSecsSinceEpoch();
    if(timestamp - timestamp_last > 1000){

        timestamp_last = timestamp;

        // Update the Treeview
        ui->treeView->viewport()->update();
    }
}

///
/// \brief MainWindow::newMessageArrived
///
void MainWindow::newMessageArrived(MESSAGE mMsg)
{
    mMavMsg.push_back(mMsg);

//    qDebug() << "MavlinkHandler: mMavMsg length:" << mMavMsg.length();

    // invoke the notifyActions
    notifyActions();

}

///
/// \brief MainWindow::notifyActions
///
void MainWindow::notifyActions()
{
    // Received MESSAGE
    if(!mMavMsg.isEmpty()){
        // Dequeue
        MESSAGE m = mMavMsg.dequeue();

        // Update the Treeview
        insertNodeMsg(m);

        // Update the plot
        replotGraph(m);

    }else{

    }
}

